home *** CD-ROM | disk | FTP | other *** search
/ Aminet 52 / Aminet 52 (2002)(GTI - Schatztruhe)[!][Dec 2002].iso / Aminet / dev / gg / ncurses-5.3.lha / ncurses-5.3 / ncurses / tty / tty_update.c < prev   
C/C++ Source or Header  |  2002-10-24  |  49KB  |  1,842 lines

  1. /****************************************************************************
  2.  * Copyright (c) 1998-2001,2002 Free Software Foundation, Inc.              *
  3.  *                                                                          *
  4.  * Permission is hereby granted, free of charge, to any person obtaining a  *
  5.  * copy of this software and associated documentation files (the            *
  6.  * "Software"), to deal in the Software without restriction, including      *
  7.  * without limitation the rights to use, copy, modify, merge, publish,      *
  8.  * distribute, distribute with modifications, sublicense, and/or sell       *
  9.  * copies of the Software, and to permit persons to whom the Software is    *
  10.  * furnished to do so, subject to the following conditions:                 *
  11.  *                                                                          *
  12.  * The above copyright notice and this permission notice shall be included  *
  13.  * in all copies or substantial portions of the Software.                   *
  14.  *                                                                          *
  15.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
  16.  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
  17.  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
  18.  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
  19.  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
  20.  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
  21.  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
  22.  *                                                                          *
  23.  * Except as contained in this notice, the name(s) of the above copyright   *
  24.  * holders shall not be used in advertising or otherwise to promote the     *
  25.  * sale, use or other dealings in this Software without prior written       *
  26.  * authorization.                                                           *
  27.  ****************************************************************************/
  28.  
  29. /****************************************************************************
  30.  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
  31.  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
  32.  ****************************************************************************/
  33.  
  34. /*-----------------------------------------------------------------
  35.  *
  36.  *    lib_doupdate.c
  37.  *
  38.  *    The routine doupdate() and its dependents.  Also _nc_outstr(),
  39.  *    so all physical output is concentrated here (except _nc_outch()
  40.  *    in lib_tputs.c).
  41.  *
  42.  *-----------------------------------------------------------------*/
  43.  
  44. #include <curses.priv.h>
  45.  
  46. #ifdef __BEOS__
  47. #undef false
  48. #undef true
  49. #include <OS.h>
  50. #endif
  51.  
  52. #if defined(TRACE) && HAVE_SYS_TIMES_H && HAVE_TIMES
  53. #define USE_TRACE_TIMES 1
  54. #else
  55. #define USE_TRACE_TIMES 0
  56. #endif
  57.  
  58. #if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
  59. #include <sys/time.h>
  60. #endif
  61.  
  62. #if USE_TRACE_TIMES
  63. #include <sys/times.h>
  64. #endif
  65.  
  66. #if USE_FUNC_POLL
  67. #elif HAVE_SELECT
  68. #if HAVE_SYS_SELECT_H
  69. #include <sys/select.h>
  70. #endif
  71. #endif
  72.  
  73. #include <term.h>
  74.  
  75. MODULE_ID("$Id: tty_update.c,v 1.177 2002/09/28 20:41:55 tom Exp $")
  76.  
  77. /*
  78.  * This define controls the line-breakout optimization.  Every once in a
  79.  * while during screen refresh, we want to check for input and abort the
  80.  * update if there's some waiting.  CHECK_INTERVAL controls the number of
  81.  * changed lines to be emitted between input checks.
  82.  *
  83.  * Note: Input-check-and-abort is no longer done if the screen is being
  84.  * updated from scratch.  This is a feature, not a bug.
  85.  */
  86. #define CHECK_INTERVAL    5
  87.  
  88. #define FILL_BCE() (SP->_coloron && !SP->_default_color && !back_color_erase)
  89.  
  90. /*
  91.  * Enable checking to see if doupdate and friends are tracking the true
  92.  * cursor position correctly.  NOTE: this is a debugging hack which will
  93.  * work ONLY on ANSI-compatible terminals!
  94.  */
  95. /* #define POSITION_DEBUG */
  96.  
  97. static inline NCURSES_CH_T ClrBlank(WINDOW *win);
  98. static int ClrBottom(int total);
  99. static void ClearScreen(NCURSES_CH_T blank);
  100. static void ClrUpdate(void);
  101. static void DelChar(int count);
  102. static void InsStr(NCURSES_CH_T * line, int count);
  103. static void TransformLine(int const lineno);
  104.  
  105. #ifdef POSITION_DEBUG
  106. /****************************************************************************
  107.  *
  108.  * Debugging code.  Only works on ANSI-standard terminals.
  109.  *
  110.  ****************************************************************************/
  111.  
  112. static void
  113. position_check(int expected_y, int expected_x, char *legend)
  114. /* check to see if the real cursor position matches the virtual */
  115. {
  116.     char buf[20];
  117.     char *s;
  118.     int y, x;
  119.  
  120.     if (!_nc_tracing || (expected_y < 0 && expected_x < 0))
  121.     return;
  122.  
  123.     _nc_flush();
  124.     memset(buf, '\0', sizeof(buf));
  125.     putp("\033[6n");        /* only works on ANSI-compatibles */
  126.     _nc_flush();
  127.     *(s = buf) = 0;
  128.     do {
  129.     int ask = sizeof(buf) - 1 - (s - buf);
  130.     int got = read(0, s, ask);
  131.     if (got == 0)
  132.         break;
  133.     s += got;
  134.     } while (strchr(buf, 'R') == 0);
  135.     _tracef("probe returned %s", _nc_visbuf(buf));
  136.  
  137.     /* try to interpret as a position report */
  138.     if (sscanf(buf, "\033[%d;%dR", &y, &x) != 2) {
  139.     _tracef("position probe failed in %s", legend);
  140.     } else {
  141.     if (expected_x < 0)
  142.         expected_x = x - 1;
  143.     if (expected_y < 0)
  144.         expected_y = y - 1;
  145.     if (y - 1 != expected_y || x - 1 != expected_x) {
  146.         beep();
  147.         tputs(tparm("\033[%d;%dH", expected_y + 1, expected_x + 1), 1, _nc_outch);
  148.         _tracef("position seen (%d, %d) doesn't match expected one (%d, %d) in %s",
  149.             y - 1, x - 1, expected_y, expected_x, legend);
  150.     } else {
  151.         _tracef("position matches OK in %s", legend);
  152.     }
  153.     }
  154. }
  155. #else
  156. #define position_check(expected_y, expected_x, legend)    /* nothing */
  157. #endif /* POSITION_DEBUG */
  158.  
  159. /****************************************************************************
  160.  *
  161.  * Optimized update code
  162.  *
  163.  ****************************************************************************/
  164.  
  165. static inline void
  166. GoTo(int const row, int const col)
  167. {
  168.     attr_t oldattr = SP->_current_attr;
  169.  
  170.     TR(TRACE_MOVE, ("GoTo(%d, %d) from (%d, %d)",
  171.             row, col, SP->_cursrow, SP->_curscol));
  172.  
  173.     position_check(SP->_cursrow, SP->_curscol, "GoTo");
  174.  
  175.     /*
  176.      * Force restore even if msgr is on when we're in an alternate
  177.      * character set -- these have a strong tendency to screw up the
  178.      * CR & LF used for local character motions!
  179.      */
  180.     if ((oldattr & A_ALTCHARSET)
  181.     || (oldattr && !move_standout_mode)) {
  182.     TR(TRACE_CHARPUT, ("turning off (%#lx) %s before move",
  183.                oldattr, _traceattr(oldattr)));
  184.     vidattr(A_NORMAL);
  185.     }
  186.  
  187.     mvcur(SP->_cursrow, SP->_curscol, row, col);
  188.     SP->_cursrow = row;
  189.     SP->_curscol = col;
  190.     position_check(SP->_cursrow, SP->_curscol, "GoTo2");
  191. }
  192.  
  193. static inline void
  194. PutAttrChar(CARG_CH_T ch)
  195. {
  196.     PUTC_DATA;
  197.     NCURSES_CH_T tilde;
  198.  
  199.     if (tilde_glitch && (CharOfD(ch) == L('~'))) {
  200.     SetChar(tilde, L('`'), AttrOfD(ch));
  201.     ch = CHREF(tilde);
  202.     }
  203.  
  204.     TR(TRACE_CHARPUT, ("PutAttrChar(%s) at (%d, %d)",
  205.                _tracech_t(ch),
  206.                SP->_cursrow, SP->_curscol));
  207.     UpdateAttrs(AttrOfD(ch));
  208. #if !USE_WIDEC_SUPPORT
  209.     /* FIXME - we do this special case for signal handling, should see how to
  210.      * make it work for wide characters.
  211.      */
  212.     if (SP->_outch != 0) {
  213.     SP->_outch((int) ch);
  214.     } else
  215. #endif
  216.     {
  217.     PUTC(CHDEREF(ch), SP->_ofp);    /* macro's fastest... */
  218. #ifdef TRACE
  219.     _nc_outchars++;
  220. #endif /* TRACE */
  221.     }
  222.     SP->_curscol++;
  223.     if (char_padding) {
  224.     TPUTS_TRACE("char_padding");
  225.     putp(char_padding);
  226.     }
  227. }
  228.  
  229. static bool
  230. check_pending(void)
  231. /* check for pending input */
  232. {
  233.     bool have_pending = FALSE;
  234.  
  235.     /*
  236.      * Only carry out this check when the flag is zero, otherwise we'll
  237.      * have the refreshing slow down drastically (or stop) if there's an
  238.      * unread character available.
  239.      */
  240.     if (SP->_fifohold != 0)
  241.     return FALSE;
  242.  
  243.     if (SP->_checkfd >= 0) {
  244. #if USE_FUNC_POLL
  245.     struct pollfd fds[1];
  246.     fds[0].fd = SP->_checkfd;
  247.     fds[0].events = POLLIN;
  248.     if (poll(fds, 1, 0) > 0) {
  249.         have_pending = TRUE;
  250.     }
  251. #elif defined(__BEOS__)
  252.     /*
  253.      * BeOS's select() is declared in socket.h, so the configure script does
  254.      * not see it.  That's just as well, since that function works only for
  255.      * sockets.  This (using snooze and ioctl) was distilled from Be's patch
  256.      * for ncurses which uses a separate thread to simulate select().
  257.      *
  258.      * FIXME: the return values from the ioctl aren't very clear if we get
  259.      * interrupted.
  260.      */
  261.     int n = 0;
  262.     int howmany = ioctl(0, 'ichr', &n);
  263.     if (howmany >= 0 && n > 0) {
  264.         have_pending = TRUE;
  265.     }
  266. #elif HAVE_SELECT
  267.     fd_set fdset;
  268.     struct timeval ktimeout;
  269.  
  270.     ktimeout.tv_sec =
  271.         ktimeout.tv_usec = 0;
  272.  
  273.     FD_ZERO(&fdset);
  274.     FD_SET(SP->_checkfd, &fdset);
  275.     if (select(SP->_checkfd + 1, &fdset, NULL, NULL, &ktimeout) != 0) {
  276.         have_pending = TRUE;
  277.     }
  278. #endif
  279.     }
  280.     if (have_pending) {
  281.     SP->_fifohold = 5;
  282.     _nc_flush();
  283.     }
  284.     return FALSE;
  285. }
  286.  
  287. /* put char at lower right corner */
  288. static void
  289. PutCharLR(const ARG_CH_T ch)
  290. {
  291.     if (!auto_right_margin) {
  292.     /* we can put the char directly */
  293.     PutAttrChar(ch);
  294.     } else if (enter_am_mode && exit_am_mode) {
  295.     /* we can suppress automargin */
  296.     TPUTS_TRACE("exit_am_mode");
  297.     putp(exit_am_mode);
  298.  
  299.     PutAttrChar(ch);
  300.     SP->_curscol--;
  301.     position_check(SP->_cursrow, SP->_curscol, "exit_am_mode");
  302.  
  303.     TPUTS_TRACE("enter_am_mode");
  304.     putp(enter_am_mode);
  305.     } else if ((enter_insert_mode && exit_insert_mode)
  306.            || insert_character || parm_ich) {
  307.     GoTo(screen_lines - 1, screen_columns - 2);
  308.     PutAttrChar(ch);
  309.     GoTo(screen_lines - 1, screen_columns - 2);
  310.     InsStr(newscr->_line[screen_lines - 1].text + screen_columns - 2, 1);
  311.     }
  312. }
  313.  
  314. static void
  315. wrap_cursor(void)
  316. {
  317.     if (eat_newline_glitch) {
  318.     /*
  319.      * xenl can manifest two different ways.  The vt100
  320.      * way is that, when you'd expect the cursor to wrap,
  321.      * it stays hung at the right margin (on top of the
  322.      * character just emitted) and doesn't wrap until the
  323.      * *next* graphic char is emitted.  The c100 way is
  324.      * to ignore LF received just after an am wrap.
  325.      *
  326.      * An aggressive way to handle this would be to
  327.      * emit CR/LF after the char and then assume the wrap
  328.      * is done, you're on the first position of the next
  329.      * line, and the terminal out of its weird state.
  330.      * Here it's safe to just tell the code that the
  331.      * cursor is in hyperspace and let the next mvcur()
  332.      * call straighten things out.
  333.      */
  334.     SP->_curscol = -1;
  335.     SP->_cursrow = -1;
  336.     } else if (auto_right_margin) {
  337.     SP->_curscol = 0;
  338.     SP->_cursrow++;
  339.     } else {
  340.     SP->_curscol--;
  341.     }
  342.     position_check(SP->_cursrow, SP->_curscol, "wrap_cursor");
  343. }
  344.  
  345. static inline void
  346. PutChar(const ARG_CH_T ch)
  347. /* insert character, handling automargin stuff */
  348. {
  349.     if (SP->_cursrow == screen_lines - 1 && SP->_curscol == screen_columns - 1)
  350.     PutCharLR(ch);
  351.     else
  352.     PutAttrChar(ch);
  353.  
  354.     if (SP->_curscol >= screen_columns)
  355.     wrap_cursor();
  356.  
  357.     position_check(SP->_cursrow, SP->_curscol, "PutChar");
  358. }
  359.  
  360. /*
  361.  * Check whether the given character can be output by clearing commands.  This
  362.  * includes test for being a space and not including any 'bad' attributes, such
  363.  * as A_REVERSE.  All attribute flags which don't affect appearance of a space
  364.  * or can be output by clearing (A_COLOR in case of bce-terminal) are excluded.
  365.  */
  366. static inline bool
  367. can_clear_with(ARG_CH_T ch)
  368. {
  369.     if (!back_color_erase && SP->_coloron) {
  370. #if NCURSES_EXT_FUNCS
  371.     if (!SP->_default_color)
  372.         return FALSE;
  373.     if (SP->_default_fg != C_MASK || SP->_default_bg != C_MASK)
  374.         return FALSE;
  375.     if (AttrOfD(ch) & A_COLOR) {
  376.         short fg, bg;
  377.         pair_content(PAIR_NUMBER(AttrOfD(ch)), &fg, &bg);
  378.         if (fg != C_MASK || bg != C_MASK)
  379.         return FALSE;
  380.     }
  381. #else
  382.     if (AttrOfD(ch) & A_COLOR)
  383.         return FALSE;
  384. #endif
  385.     }
  386.     return (ISBLANK(CHDEREF(ch)) &&
  387.         (AttrOfD(ch) & ~(NONBLANK_ATTR | A_COLOR)) == BLANK_ATTR);
  388. }
  389.  
  390. /*
  391.  * Issue a given span of characters from an array.
  392.  * Must be functionally equivalent to:
  393.  *    for (i = 0; i < num; i++)
  394.  *        PutChar(ntext[i]);
  395.  * but can leave the cursor positioned at the middle of the interval.
  396.  *
  397.  * Returns: 0 - cursor is at the end of interval
  398.  *        1 - cursor is somewhere in the middle
  399.  *
  400.  * This code is optimized using ech and rep.
  401.  */
  402. static int
  403. EmitRange(const NCURSES_CH_T * ntext, int num)
  404. {
  405.     int i;
  406.  
  407.     if (erase_chars || repeat_char) {
  408.     while (num > 0) {
  409.         int runcount;
  410.         NCURSES_CH_T ntext0;
  411.  
  412.         while (num > 1 && !CharEq(ntext[0], ntext[1])) {
  413.         PutChar(CHREF(ntext[0]));
  414.         ntext++;
  415.         num--;
  416.         }
  417.         ntext0 = ntext[0];
  418.         if (num == 1) {
  419.         PutChar(CHREF(ntext0));
  420.         return 0;
  421.         }
  422.         runcount = 2;
  423.  
  424.         while (runcount < num && CharEq(ntext[runcount], ntext0))
  425.         runcount++;
  426.  
  427.         /*
  428.          * The cost expression in the middle isn't exactly right.
  429.          * _cup_ch_cost is an upper bound on the cost for moving to the
  430.          * end of the erased area, but not the cost itself (which we
  431.          * can't compute without emitting the move).  This may result
  432.          * in erase_chars not getting used in some situations for
  433.          * which it would be marginally advantageous.
  434.          */
  435.         if (erase_chars
  436.         && runcount > SP->_ech_cost + SP->_cup_ch_cost
  437.         && can_clear_with(CHREF(ntext0))) {
  438.         UpdateAttrs(AttrOf(ntext0));
  439.         putp(tparm(erase_chars, runcount));
  440.  
  441.         /*
  442.          * If this is the last part of the given interval,
  443.          * don't bother moving cursor, since it can be the
  444.          * last update on the line.
  445.          */
  446.         if (runcount < num) {
  447.             GoTo(SP->_cursrow, SP->_curscol + runcount);
  448.         } else {
  449.             return 1;    /* cursor stays in the middle */
  450.         }
  451.         } else if (repeat_char && runcount > SP->_rep_cost) {
  452.         bool wrap_possible = (SP->_curscol + runcount >= screen_columns);
  453.         int rep_count = runcount;
  454.  
  455.         if (wrap_possible)
  456.             rep_count--;
  457.  
  458.         UpdateAttrs(AttrOf(ntext0));
  459.         putp(tparm(repeat_char, CharOf(ntext0), rep_count));
  460.         SP->_curscol += rep_count;
  461.  
  462.         if (wrap_possible)
  463.             PutChar(CHREF(ntext0));
  464.         } else {
  465.         for (i = 0; i < runcount; i++)
  466.             PutChar(CHREF(ntext[i]));
  467.         }
  468.         ntext += runcount;
  469.         num -= runcount;
  470.     }
  471.     return 0;
  472.     }
  473.  
  474.     for (i = 0; i < num; i++)
  475.     PutChar(CHREF(ntext[i]));
  476.     return 0;
  477. }
  478.  
  479. /*
  480.  * Output the line in the given range [first .. last]
  481.  *
  482.  * If there's a run of identical characters that's long enough to justify
  483.  * cursor movement, use that also.
  484.  *
  485.  * Returns: same as EmitRange
  486.  */
  487. static int
  488. PutRange(const NCURSES_CH_T * otext,
  489.      const NCURSES_CH_T * ntext,
  490.      int row,
  491.      int first, int last)
  492. {
  493.     int j, run;
  494.  
  495.     TR(TRACE_CHARPUT, ("PutRange(%p, %p, %d, %d, %d)",
  496.                otext, ntext, row, first, last));
  497.  
  498.     if (otext != ntext
  499.     && (last - first + 1) > SP->_inline_cost) {
  500.     for (j = first, run = 0; j <= last; j++) {
  501.         if (!run && isnac(otext[j]))
  502.         continue;
  503.         if (CharEq(otext[j], ntext[j])) {
  504.         run++;
  505.         } else {
  506.         if (run > SP->_inline_cost) {
  507.             int before_run = (j - run);
  508.             EmitRange(ntext + first, before_run - first);
  509.             GoTo(row, first = j);
  510.         }
  511.         run = 0;
  512.         }
  513.     }
  514.     }
  515.     return EmitRange(ntext + first, last - first + 1);
  516. }
  517.  
  518. /* leave unbracketed here so 'indent' works */
  519. #define MARK_NOCHANGE(win,row) \
  520.         win->_line[row].firstchar = _NOCHANGE; \
  521.         win->_line[row].lastchar = _NOCHANGE; \
  522.         if_USE_SCROLL_HINTS(win->_line[row].oldindex = row)
  523.  
  524. NCURSES_EXPORT(int)
  525. doupdate(void)
  526. {
  527.     int i;
  528.     int nonempty;
  529. #if USE_TRACE_TIMES
  530.     struct tms before, after;
  531. #endif /* USE_TRACE_TIMES */
  532.  
  533.     T((T_CALLED("doupdate()")));
  534.  
  535. #ifdef TRACE
  536.     if (_nc_tracing & TRACE_UPDATE) {
  537.     if (curscr->_clear)
  538.         _tracef("curscr is clear");
  539.     else
  540.         _tracedump("curscr", curscr);
  541.     _tracedump("newscr", newscr);
  542.     }
  543. #endif /* TRACE */
  544.  
  545.     _nc_signal_handler(FALSE);
  546.  
  547.     if (SP->_fifohold)
  548.     SP->_fifohold--;
  549.  
  550. #if USE_SIZECHANGE
  551.     if (SP->_endwin || SP->_sig_winch) {
  552.     /*
  553.      * This is a transparent extension:  XSI does not address it,
  554.      * and applications need not know that ncurses can do it.
  555.      *
  556.      * Check if the terminal size has changed while curses was off
  557.      * (this can happen in an xterm, for example), and resize the
  558.      * ncurses data structures accordingly.
  559.      */
  560.     _nc_update_screensize();
  561.     }
  562. #endif
  563.  
  564.     if (SP->_endwin) {
  565.  
  566.     T(("coming back from shell mode"));
  567.     reset_prog_mode();
  568.  
  569.     _nc_mvcur_resume();
  570.     _nc_screen_resume();
  571.     SP->_mouse_resume(SP);
  572.  
  573.     SP->_endwin = FALSE;
  574.     }
  575. #if USE_TRACE_TIMES
  576.     /* zero the metering machinery */
  577.     _nc_outchars = 0;
  578.     (void) times(&before);
  579. #endif /* USE_TRACE_TIMES */
  580.  
  581.     /*
  582.      * This is the support for magic-cookie terminals.  The
  583.      * theory: we scan the virtual screen looking for attribute
  584.      * turnons.  Where we find one, check to make sure it's
  585.      * realizable by seeing if the required number of
  586.      * un-attributed blanks are present before and after the
  587.      * attributed range; try to shift the range boundaries over
  588.      * blanks (not changing the screen display) so this becomes
  589.      * true.  If it is, shift the beginning attribute change
  590.      * appropriately (the end one, if we've gotten this far, is
  591.      * guaranteed room for its cookie). If not, nuke the added
  592.      * attributes out of the span.
  593.      */
  594. #if USE_XMC_SUPPORT
  595.     if (magic_cookie_glitch > 0) {
  596.     int j, k;
  597.     attr_t rattr = A_NORMAL;
  598.  
  599.     for (i = 0; i < screen_lines; i++) {
  600.         for (j = 0; j < screen_columns; j++) {
  601.         bool failed = FALSE;
  602.         attr_t turnon = AttrOf(newscr->_line[i].text[j]) & ~rattr;
  603.  
  604.         /* is an attribute turned on here? */
  605.         if (turnon == 0) {
  606.             rattr = AttrOf(newscr->_line[i].text[j]);
  607.             continue;
  608.         }
  609.  
  610.         TR(TRACE_ATTRS, ("At (%d, %d): from %s...", i, j, _traceattr(rattr)));
  611.         TR(TRACE_ATTRS, ("...to %s", _traceattr(turnon)));
  612.  
  613.         /*
  614.          * If the attribute change location is a blank with a
  615.          * "safe" attribute, undo the attribute turnon.  This may
  616.          * ensure there's enough room to set the attribute before
  617.          * the first non-blank in the run.
  618.          */
  619. #define SAFE(a)    (!((a) & (attr_t)~NONBLANK_ATTR))
  620.         if (ISBLANK(newscr->_line[i].text[j]) && SAFE(turnon)) {
  621.             RemAttr(newscr->_line[i].text[j], turnon);
  622.             continue;
  623.         }
  624.  
  625.         /* check that there's enough room at start of span */
  626.         for (k = 1; k <= magic_cookie_glitch; k++) {
  627.             if (j - k < 0
  628.             || !ISBLANK(newscr->_line[i].text[j - k])
  629.             || !SAFE(AttrOf(newscr->_line[i].text[j - k])))
  630.             failed = TRUE;
  631.         }
  632.         if (!failed) {
  633.             bool end_onscreen = FALSE;
  634.             int m, n = j;
  635.  
  636.             /* find end of span, if it's onscreen */
  637.             for (m = i; m < screen_lines; m++) {
  638.             for (; n < screen_columns; n++) {
  639.                 if (AttrOf(newscr->_line[m].text[n]) == rattr) {
  640.                 end_onscreen = TRUE;
  641.                 TR(TRACE_ATTRS,
  642.                    ("Range attributed with %s ends at (%d, %d)",
  643.                     _traceattr(turnon), m, n));
  644.                 goto foundit;
  645.                 }
  646.             }
  647.             n = 0;
  648.             }
  649.             TR(TRACE_ATTRS,
  650.                ("Range attributed with %s ends offscreen",
  651.             _traceattr(turnon)));
  652.           foundit:;
  653.  
  654.             if (end_onscreen) {
  655.             NCURSES_CH_T *lastline = newscr->_line[m].text;
  656.  
  657.             /*
  658.              * If there are safely-attributed blanks at the
  659.              * end of the range, shorten the range.  This will
  660.              * help ensure that there is enough room at end
  661.              * of span.
  662.              */
  663.             while (n >= 0
  664.                    && ISBLANK(lastline[n])
  665.                    && SAFE(AttrOf(lastline[n])))
  666.                 RemAttr(lastline[n--], turnon);
  667.  
  668.             /* check that there's enough room at end of span */
  669.             for (k = 1; k <= magic_cookie_glitch; k++)
  670.                 if (n + k >= screen_columns
  671.                 || !ISBLANK(lastline[n + k])
  672.                 || !SAFE(AttrOf(lastline[n + k])))
  673.                 failed = TRUE;
  674.             }
  675.         }
  676.  
  677.         if (failed) {
  678.             int p, q = j;
  679.  
  680.             TR(TRACE_ATTRS,
  681.                ("Clearing %s beginning at (%d, %d)",
  682.             _traceattr(turnon), i, j));
  683.  
  684.             /* turn off new attributes over span */
  685.             for (p = i; p < screen_lines; p++) {
  686.             for (; q < screen_columns; q++) {
  687.                 if (AttrOf(newscr->_line[p].text[q]) == rattr)
  688.                 goto foundend;
  689.                 RemAttr(newscr->_line[p].text[q], turnon);
  690.             }
  691.             q = 0;
  692.             }
  693.           foundend:;
  694.         } else {
  695.             TR(TRACE_ATTRS,
  696.                ("Cookie space for %s found before (%d, %d)",
  697.             _traceattr(turnon), i, j));
  698.  
  699.             /*
  700.              * back up the start of range so there's room
  701.              * for cookies before the first nonblank character
  702.              */
  703.             for (k = 1; k <= magic_cookie_glitch; k++)
  704.             AddAttr(newscr->_line[i].text[j - k], turnon);
  705.         }
  706.  
  707.         rattr = AttrOf(newscr->_line[i].text[j]);
  708.         }
  709.     }
  710.  
  711. #ifdef TRACE
  712.     /* show altered highlights after magic-cookie check */
  713.     if (_nc_tracing & TRACE_UPDATE) {
  714.         _tracef("After magic-cookie check...");
  715.         _tracedump("newscr", newscr);
  716.     }
  717. #endif /* TRACE */
  718.     }
  719. #endif /* USE_XMC_SUPPORT */
  720.  
  721.     nonempty = 0;
  722.     if (curscr->_clear || newscr->_clear) {    /* force refresh ? */
  723.     TR(TRACE_UPDATE, ("clearing and updating from scratch"));
  724.     ClrUpdate();
  725.     curscr->_clear = FALSE;    /* reset flag */
  726.     newscr->_clear = FALSE;    /* reset flag */
  727.     } else {
  728.     int changedlines = CHECK_INTERVAL;
  729.  
  730.     if (check_pending())
  731.         goto cleanup;
  732.  
  733.     nonempty = min(screen_lines, newscr->_maxy + 1);
  734.  
  735.     if (SP->_scrolling) {
  736.         _nc_scroll_optimize();
  737.     }
  738.  
  739.     nonempty = ClrBottom(nonempty);
  740.  
  741.     TR(TRACE_UPDATE, ("Transforming lines, nonempty %d", nonempty));
  742.     for (i = 0; i < nonempty; i++) {
  743.         /*
  744.          * Here is our line-breakout optimization.
  745.          */
  746.         if (changedlines == CHECK_INTERVAL) {
  747.         if (check_pending())
  748.             goto cleanup;
  749.         changedlines = 0;
  750.         }
  751.  
  752.         /*
  753.          * newscr->line[i].firstchar is normally set
  754.          * by wnoutrefresh.  curscr->line[i].firstchar
  755.          * is normally set by _nc_scroll_window in the
  756.          * vertical-movement optimization code,
  757.          */
  758.         if (newscr->_line[i].firstchar != _NOCHANGE
  759.         || curscr->_line[i].firstchar != _NOCHANGE) {
  760.         TransformLine(i);
  761.         changedlines++;
  762.         }
  763.  
  764.         /* mark line changed successfully */
  765.         if (i <= newscr->_maxy) {
  766.         MARK_NOCHANGE(newscr, i);
  767.         }
  768.         if (i <= curscr->_maxy) {
  769.         MARK_NOCHANGE(curscr, i);
  770.         }
  771.     }
  772.     }
  773.  
  774.     /* put everything back in sync */
  775.     for (i = nonempty; i <= newscr->_maxy; i++) {
  776.     MARK_NOCHANGE(newscr, i);
  777.     }
  778.     for (i = nonempty; i <= curscr->_maxy; i++) {
  779.     MARK_NOCHANGE(curscr, i);
  780.     }
  781.  
  782.     if (!newscr->_leaveok) {
  783.     curscr->_curx = newscr->_curx;
  784.     curscr->_cury = newscr->_cury;
  785.  
  786.     GoTo(curscr->_cury, curscr->_curx);
  787.     }
  788.  
  789.   cleanup:
  790.     /*
  791.      * Keep the physical screen in normal mode in case we get other
  792.      * processes writing to the screen.
  793.      */
  794.     UpdateAttrs(A_NORMAL);
  795.  
  796.     _nc_flush();
  797.     curscr->_attrs = newscr->_attrs;
  798.  
  799. #if USE_TRACE_TIMES
  800.     (void) times(&after);
  801.     TR(TRACE_TIMES,
  802.        ("Update cost: %ld chars, %ld clocks system time, %ld clocks user time",
  803.     _nc_outchars,
  804.     (long) (after.tms_stime - before.tms_stime),
  805.     (long) (after.tms_utime - before.tms_utime)));
  806. #endif /* USE_TRACE_TIMES */
  807.  
  808.     _nc_signal_handler(TRUE);
  809.  
  810.     returnCode(OK);
  811. }
  812.  
  813. /*
  814.  *    ClrBlank(win)
  815.  *
  816.  *    Returns the attributed character that corresponds to the "cleared"
  817.  *    screen.  If the terminal has the back-color-erase feature, this will be
  818.  *    colored according to the wbkgd() call.
  819.  *
  820.  *    We treat 'curscr' specially because it isn't supposed to be set directly
  821.  *    in the wbkgd() call.  Assume 'stdscr' for this case.
  822.  */
  823. #define BCE_ATTRS (A_NORMAL|A_COLOR)
  824. #define BCE_BKGD(win) (((win) == curscr ? stdscr : (win))->_nc_bkgd)
  825.  
  826. static inline NCURSES_CH_T
  827. ClrBlank(WINDOW *win)
  828. {
  829.     NCURSES_CH_T blank = NewChar(BLANK_TEXT);
  830.     if (back_color_erase)
  831.     AddAttr(blank, (AttrOf(BCE_BKGD(win)) & BCE_ATTRS));
  832.     return blank;
  833. }
  834.  
  835. /*
  836. **    ClrUpdate()
  837. **
  838. **    Update by clearing and redrawing the entire screen.
  839. **
  840. */
  841.  
  842. static void
  843. ClrUpdate(void)
  844. {
  845.     int i;
  846.     NCURSES_CH_T blank = ClrBlank(stdscr);
  847.     int nonempty = min(screen_lines, newscr->_maxy + 1);
  848.  
  849.     TR(TRACE_UPDATE, ("ClrUpdate() called"));
  850.  
  851.     ClearScreen(blank);
  852.  
  853.     TR(TRACE_UPDATE, ("updating screen from scratch"));
  854.  
  855.     nonempty = ClrBottom(nonempty);
  856.  
  857.     for (i = 0; i < nonempty; i++)
  858.     TransformLine(i);
  859. }
  860.  
  861. /*
  862. **    ClrToEOL(blank)
  863. **
  864. **    Clear to end of current line, starting at the cursor position
  865. */
  866.  
  867. static void
  868. ClrToEOL(NCURSES_CH_T blank, bool needclear)
  869. {
  870.     int j;
  871.  
  872.     if (curscr != 0
  873.     && SP->_cursrow >= 0) {
  874.     for (j = SP->_curscol; j < screen_columns; j++) {
  875.         if (j >= 0) {
  876.         NCURSES_CH_T *cp = &(curscr->_line[SP->_cursrow].text[j]);
  877.  
  878.         if (!CharEq(*cp, blank)) {
  879.             *cp = blank;
  880.             needclear = TRUE;
  881.         }
  882.         }
  883.     }
  884.     } else {
  885.     needclear = TRUE;
  886.     }
  887.  
  888.     if (needclear) {
  889.     UpdateAttrs(AttrOf(blank));
  890.     TPUTS_TRACE("clr_eol");
  891.     if (SP->_el_cost > (screen_columns - SP->_curscol)) {
  892.         int count = (screen_columns - SP->_curscol);
  893.         while (count-- > 0)
  894.         PutChar(CHREF(blank));
  895.     } else {
  896.         putp(clr_eol);
  897.     }
  898.     }
  899. }
  900.  
  901. /*
  902. **    ClrToEOS(blank)
  903. **
  904. **    Clear to end of screen, starting at the cursor position
  905. */
  906.  
  907. static void
  908. ClrToEOS(NCURSES_CH_T blank)
  909. {
  910.     int row, col;
  911.  
  912.     row = SP->_cursrow;
  913.     col = SP->_curscol;
  914.  
  915.     UpdateAttrs(AttrOf(blank));
  916.     TPUTS_TRACE("clr_eos");
  917.     tputs(clr_eos, screen_lines - row, _nc_outch);
  918.  
  919.     while (col < screen_columns)
  920.     curscr->_line[row].text[col++] = blank;
  921.  
  922.     for (row++; row < screen_lines; row++) {
  923.     for (col = 0; col < screen_columns; col++)
  924.         curscr->_line[row].text[col] = blank;
  925.     }
  926. }
  927.  
  928. /*
  929.  *    ClrBottom(total)
  930.  *
  931.  *    Test if clearing the end of the screen would satisfy part of the
  932.  *    screen-update.  Do this by scanning backwards through the lines in the
  933.  *    screen, checking if each is blank, and one or more are changed.
  934.  */
  935. static int
  936. ClrBottom(int total)
  937. {
  938.     int row;
  939.     int col;
  940.     int top = total;
  941.     int last = min(screen_columns, newscr->_maxx + 1);
  942.     NCURSES_CH_T blank = ClrBlank(stdscr);
  943.     bool ok;
  944.  
  945.     if (clr_eos && can_clear_with(CHREF(blank))) {
  946.  
  947.     for (row = total - 1; row >= 0; row--) {
  948.         for (col = 0, ok = TRUE; ok && col < last; col++) {
  949.         ok = (CharEq(newscr->_line[row].text[col], blank));
  950.         }
  951.         if (!ok)
  952.         break;
  953.  
  954.         for (col = 0; ok && col < last; col++) {
  955.         ok = (CharEq(curscr->_line[row].text[col], blank));
  956.         }
  957.         if (!ok)
  958.         top = row;
  959.     }
  960.  
  961.     /* don't use clr_eos for just one line if clr_eol available */
  962.     if (top < total - 1 || (top < total && !clr_eol && !clr_bol)) {
  963.         GoTo(top, 0);
  964.         ClrToEOS(blank);
  965.         total = top;
  966.         if (SP->oldhash && SP->newhash) {
  967.         for (row = top; row < screen_lines; row++)
  968.             SP->oldhash[row] = SP->newhash[row];
  969.         }
  970.     }
  971.     }
  972.     return total;
  973. }
  974.  
  975. #if USE_XMC_SUPPORT
  976. #if USE_WIDEC_SUPPORT
  977. static inline bool
  978. check_xmc_transition(NCURSES_CH_T * a, NCURSES_CH_T * b)
  979. {
  980.     if (((a->attr ^ b->attr) & ~(a->attr) & SP->_xmc_triggers) != 0) {
  981.     return TRUE;
  982.     }
  983.     return FALSE;
  984. }
  985. #define xmc_turn_on(a,b) check_xmc_transition(&(a), &(b))
  986. #else
  987. #define xmc_turn_on(a,b) ((((a)^(b)) & ~(a) & SP->_xmc_triggers) != 0)
  988. #endif
  989.  
  990. #define xmc_new(r,c) newscr->_line[r].text[c]
  991. #define xmc_turn_off(a,b) xmc_turn_on(b,a)
  992. #endif /* USE_XMC_SUPPORT */
  993.  
  994. /*
  995. **    TransformLine(lineno)
  996. **
  997. **    Transform the given line in curscr to the one in newscr, using
  998. **    Insert/Delete Character if _nc_idcok && has_ic().
  999. **
  1000. **        firstChar = position of first different character in line
  1001. **        oLastChar = position of last different character in old line
  1002. **        nLastChar = position of last different character in new line
  1003. **
  1004. **        move to firstChar
  1005. **        overwrite chars up to min(oLastChar, nLastChar)
  1006. **        if oLastChar < nLastChar
  1007. **            insert newLine[oLastChar+1..nLastChar]
  1008. **        else
  1009. **            delete oLastChar - nLastChar spaces
  1010. */
  1011.  
  1012. static void
  1013. TransformLine(int const lineno)
  1014. {
  1015.     int firstChar, oLastChar, nLastChar;
  1016.     NCURSES_CH_T *newLine = newscr->_line[lineno].text;
  1017.     NCURSES_CH_T *oldLine = curscr->_line[lineno].text;
  1018.     int n;
  1019.     bool attrchanged = FALSE;
  1020.  
  1021.     TR(TRACE_UPDATE, ("TransformLine(%d) called", lineno));
  1022.  
  1023.     /* copy new hash value to old one */
  1024.     if (SP->oldhash && SP->newhash)
  1025.     SP->oldhash[lineno] = SP->newhash[lineno];
  1026.  
  1027. #define ColorOf(n) (AttrOf(n) & A_COLOR)
  1028. #define unColor(n) (AttrOf(n) & ALL_BUT_COLOR)
  1029.     /*
  1030.      * If we have colors, there is the possibility of having two color pairs
  1031.      * that display as the same colors.  For instance, Lynx does this.  Check
  1032.      * for this case, and update the old line with the new line's colors when
  1033.      * they are equivalent.
  1034.      */
  1035.     if (SP->_coloron) {
  1036.     attr_t oldColor;
  1037.     attr_t newColor;
  1038.     int oldPair;
  1039.     int newPair;
  1040.  
  1041.     for (n = 0; n < screen_columns; n++) {
  1042.         if (!CharEq(newLine[n], oldLine[n])) {
  1043.         oldColor = ColorOf(oldLine[n]);
  1044.         newColor = ColorOf(newLine[n]);
  1045.         if (oldColor != newColor
  1046.             && unColor(oldLine[n]) == unColor(newLine[n])) {
  1047.             oldPair = PAIR_NUMBER(oldColor);
  1048.             newPair = PAIR_NUMBER(newColor);
  1049.             if (oldPair < COLOR_PAIRS
  1050.             && newPair < COLOR_PAIRS
  1051.             && SP->_color_pairs[oldPair] == SP->_color_pairs[newPair]) {
  1052.             RemAttr(oldLine[n], A_COLOR);
  1053.             AddAttr(oldLine[n], ColorOf(newLine[n]));
  1054.             }
  1055.         }
  1056.         }
  1057.     }
  1058.     }
  1059.  
  1060.     if (ceol_standout_glitch && clr_eol) {
  1061.     firstChar = 0;
  1062.     while (firstChar < screen_columns) {
  1063.         if (AttrOf(newLine[firstChar]) != AttrOf(oldLine[firstChar]))
  1064.         attrchanged = TRUE;
  1065.         firstChar++;
  1066.     }
  1067.     }
  1068.  
  1069.     firstChar = 0;
  1070.  
  1071.     if (attrchanged) {        /* we may have to disregard the whole line */
  1072.     GoTo(lineno, firstChar);
  1073.     ClrToEOL(ClrBlank(curscr), FALSE);
  1074.     PutRange(oldLine, newLine, lineno, 0, (screen_columns - 1));
  1075. #if USE_XMC_SUPPORT
  1076.  
  1077.     /*
  1078.      * This is a very simple loop to paint characters which may have the
  1079.      * magic cookie glitch embedded.  It doesn't know much about video
  1080.      * attributes which are continued from one line to the next.  It
  1081.      * assumes that we have filtered out requests for attribute changes
  1082.      * that do not get mapped to blank positions.
  1083.      *
  1084.      * FIXME: we are not keeping track of where we put the cookies, so this
  1085.      * will work properly only once, since we may overwrite a cookie in a
  1086.      * following operation.
  1087.      */
  1088.     } else if (magic_cookie_glitch > 0) {
  1089.     GoTo(lineno, firstChar);
  1090.     for (n = 0; n < screen_columns; n++) {
  1091.         int m = n + magic_cookie_glitch;
  1092.  
  1093.         /* check for turn-on:
  1094.          * If we are writing an attributed blank, where the
  1095.          * previous cell is not attributed.
  1096.          */
  1097.         if (ISBLANK(newLine[n])
  1098.         && ((n > 0
  1099.              && xmc_turn_on(newLine[n - 1], newLine[n]))
  1100.             || (n == 0
  1101.             && lineno > 0
  1102.             && xmc_turn_on(xmc_new(lineno - 1, screen_columns - 1),
  1103.                        newLine[n])))) {
  1104.         n = m;
  1105.         }
  1106.  
  1107.         PutChar(CHREF(newLine[n]));
  1108.  
  1109.         /* check for turn-off:
  1110.          * If we are writing an attributed non-blank, where the
  1111.          * next cell is blank, and not attributed.
  1112.          */
  1113.         if (!ISBLANK(newLine[n])
  1114.         && ((n + 1 < screen_columns
  1115.              && xmc_turn_off(newLine[n], newLine[n + 1]))
  1116.             || (n + 1 >= screen_columns
  1117.             && lineno + 1 < screen_lines
  1118.             && xmc_turn_off(newLine[n], xmc_new(lineno + 1, 0))))) {
  1119.         n = m;
  1120.         }
  1121.  
  1122.     }
  1123. #endif
  1124.     } else {
  1125.     NCURSES_CH_T blank;
  1126.  
  1127.     /* find the first differing character */
  1128.     while (firstChar < screen_columns &&
  1129.            CharEq(newLine[firstChar], oldLine[firstChar]))
  1130.         firstChar++;
  1131.  
  1132.     /* if there wasn't one, we're done */
  1133.     if (firstChar >= screen_columns)
  1134.         return;
  1135.  
  1136.     /* it may be cheap to clear leading whitespace with clr_bol */
  1137.     if (clr_bol && (blank = newLine[0], can_clear_with(CHREF(blank)))) {
  1138.         int oFirstChar, nFirstChar;
  1139.  
  1140.         for (oFirstChar = 0; oFirstChar < screen_columns; oFirstChar++)
  1141.         if (!CharEq(oldLine[oFirstChar], blank))
  1142.             break;
  1143.         for (nFirstChar = 0; nFirstChar < screen_columns; nFirstChar++)
  1144.         if (!CharEq(newLine[nFirstChar], blank))
  1145.             break;
  1146.  
  1147.         if (nFirstChar > oFirstChar + SP->_el1_cost) {
  1148.         if (nFirstChar >= screen_columns && SP->_el_cost <= SP->_el1_cost) {
  1149.             GoTo(lineno, 0);
  1150.             UpdateAttrs(AttrOf(blank));
  1151.             TPUTS_TRACE("clr_eol");
  1152.             putp(clr_eol);
  1153.         } else {
  1154.             GoTo(lineno, nFirstChar - 1);
  1155.             UpdateAttrs(AttrOf(blank));
  1156.             TPUTS_TRACE("clr_bol");
  1157.             putp(clr_bol);
  1158.         }
  1159.  
  1160.         while (firstChar < nFirstChar)
  1161.             oldLine[firstChar++] = blank;
  1162.  
  1163.         if (firstChar >= screen_columns)
  1164.             return;
  1165.         }
  1166.     }
  1167.  
  1168.     blank = newLine[screen_columns - 1];
  1169.  
  1170.     if (!can_clear_with(CHREF(blank))) {
  1171.         /* find the last differing character */
  1172.         nLastChar = screen_columns - 1;
  1173.  
  1174.         while (nLastChar > firstChar
  1175.            && CharEq(newLine[nLastChar], oldLine[nLastChar]))
  1176.         nLastChar--;
  1177.  
  1178.         if (nLastChar >= firstChar) {
  1179.         GoTo(lineno, firstChar);
  1180.         PutRange(oldLine, newLine, lineno, firstChar, nLastChar);
  1181.         memcpy(oldLine + firstChar,
  1182.                newLine + firstChar,
  1183.                (nLastChar - firstChar + 1) * sizeof(NCURSES_CH_T));
  1184.         }
  1185.         return;
  1186.     }
  1187.  
  1188.     /* find last non-blank character on old line */
  1189.     oLastChar = screen_columns - 1;
  1190.     while (oLastChar > firstChar && CharEq(oldLine[oLastChar], blank))
  1191.         oLastChar--;
  1192.  
  1193.     /* find last non-blank character on new line */
  1194.     nLastChar = screen_columns - 1;
  1195.     while (nLastChar > firstChar && CharEq(newLine[nLastChar], blank))
  1196.         nLastChar--;
  1197.  
  1198.     if ((nLastChar == firstChar)
  1199.         && (SP->_el_cost < (oLastChar - nLastChar))) {
  1200.         GoTo(lineno, firstChar);
  1201.         if (!CharEq(newLine[firstChar], blank))
  1202.         PutChar(CHREF(newLine[firstChar]));
  1203.         ClrToEOL(blank, FALSE);
  1204.     } else if ((nLastChar != oLastChar)
  1205.            && (!CharEq(newLine[nLastChar], oldLine[oLastChar])
  1206.                || !(_nc_idcok && has_ic()))) {
  1207.         GoTo(lineno, firstChar);
  1208.         if ((oLastChar - nLastChar) > SP->_el_cost) {
  1209.         if (PutRange(oldLine, newLine, lineno, firstChar, nLastChar))
  1210.             GoTo(lineno, nLastChar + 1);
  1211.         ClrToEOL(blank, FALSE);
  1212.         } else {
  1213.         n = max(nLastChar, oLastChar);
  1214.         PutRange(oldLine, newLine, lineno, firstChar, n);
  1215.         }
  1216.     } else {
  1217.         int nLastNonblank = nLastChar;
  1218.         int oLastNonblank = oLastChar;
  1219.  
  1220.         /* find the last characters that really differ */
  1221.         /* can be -1 if no characters differ */
  1222.         while (CharEq(newLine[nLastChar], oldLine[oLastChar])) {
  1223.         /* don't split a wide char */
  1224.         if (isnac(newLine[nLastChar]) &&
  1225.             !CharEq(newLine[nLastChar - 1], oldLine[oLastChar - 1]))
  1226.             break;
  1227.         nLastChar--;
  1228.         oLastChar--;
  1229.         if (nLastChar == -1 || oLastChar == -1)
  1230.             break;
  1231.         }
  1232.  
  1233.         n = min(oLastChar, nLastChar);
  1234.         if (n >= firstChar) {
  1235.         GoTo(lineno, firstChar);
  1236.         PutRange(oldLine, newLine, lineno, firstChar, n);
  1237.         }
  1238.  
  1239.         if (oLastChar < nLastChar) {
  1240.         int m = max(nLastNonblank, oLastNonblank);
  1241.         GoTo(lineno, n + 1);
  1242.         if (InsCharCost(nLastChar - oLastChar)
  1243.             > (m - n)) {
  1244.             PutRange(oldLine, newLine, lineno, n + 1, m);
  1245.         } else {
  1246.             InsStr(&newLine[n + 1], nLastChar - oLastChar);
  1247.         }
  1248.         } else if (oLastChar > nLastChar) {
  1249.         GoTo(lineno, n + 1);
  1250.         if (DelCharCost(oLastChar - nLastChar)
  1251.             > SP->_el_cost + nLastNonblank - (n + 1)) {
  1252.             if (PutRange(oldLine, newLine, lineno,
  1253.                  n + 1, nLastNonblank))
  1254.             GoTo(lineno, nLastNonblank + 1);
  1255.             ClrToEOL(blank, FALSE);
  1256.         } else {
  1257.             /*
  1258.              * The delete-char sequence will
  1259.              * effectively shift in blanks from the
  1260.              * right margin of the screen.  Ensure
  1261.              * that they are the right color by
  1262.              * setting the video attributes from
  1263.              * the last character on the row.
  1264.              */
  1265.             UpdateAttrs(AttrOf(blank));
  1266.             DelChar(oLastChar - nLastChar);
  1267.         }
  1268.         }
  1269.     }
  1270.     }
  1271.  
  1272.     /* update the code's internal representation */
  1273.     if (screen_columns > firstChar)
  1274.     memcpy(oldLine + firstChar,
  1275.            newLine + firstChar,
  1276.            (screen_columns - firstChar) * sizeof(NCURSES_CH_T));
  1277. }
  1278.  
  1279. /*
  1280. **    ClearScreen(blank)
  1281. **
  1282. **    Clear the physical screen and put cursor at home
  1283. **
  1284. */
  1285.  
  1286. static void
  1287. ClearScreen(NCURSES_CH_T blank)
  1288. {
  1289.     int i, j;
  1290.     bool fast_clear = (clear_screen || clr_eos || clr_eol);
  1291.  
  1292.     TR(TRACE_UPDATE, ("ClearScreen() called"));
  1293.  
  1294. #if NCURSES_EXT_FUNCS
  1295.     if (SP->_coloron
  1296.     && !SP->_default_color) {
  1297.     _nc_do_color((int) COLOR_PAIR(SP->_current_attr), 0, FALSE, _nc_outch);
  1298.     if (!back_color_erase) {
  1299.         fast_clear = FALSE;
  1300.     }
  1301.     }
  1302. #endif
  1303.  
  1304.     if (fast_clear) {
  1305.     if (clear_screen) {
  1306.         UpdateAttrs(AttrOf(blank));
  1307.         TPUTS_TRACE("clear_screen");
  1308.         putp(clear_screen);
  1309.         SP->_cursrow = SP->_curscol = 0;
  1310.         position_check(SP->_cursrow, SP->_curscol, "ClearScreen");
  1311.     } else if (clr_eos) {
  1312.         SP->_cursrow = SP->_curscol = -1;
  1313.         GoTo(0, 0);
  1314.  
  1315.         UpdateAttrs(AttrOf(blank));
  1316.         TPUTS_TRACE("clr_eos");
  1317.         putp(clr_eos);
  1318.     } else if (clr_eol) {
  1319.         SP->_cursrow = SP->_curscol = -1;
  1320.  
  1321.         UpdateAttrs(AttrOf(blank));
  1322.         for (i = 0; i < screen_lines; i++) {
  1323.         GoTo(i, 0);
  1324.         TPUTS_TRACE("clr_eol");
  1325.         putp(clr_eol);
  1326.         }
  1327.         GoTo(0, 0);
  1328.     }
  1329.     } else {
  1330.     UpdateAttrs(AttrOf(blank));
  1331.     for (i = 0; i < screen_lines; i++) {
  1332.         GoTo(i, 0);
  1333.         for (j = 0; j < screen_columns; j++)
  1334.         PutChar(CHREF(blank));
  1335.     }
  1336.     GoTo(0, 0);
  1337.     }
  1338.  
  1339.     for (i = 0; i < screen_lines; i++) {
  1340.     for (j = 0; j < screen_columns; j++)
  1341.         curscr->_line[i].text[j] = blank;
  1342.     }
  1343.  
  1344.     TR(TRACE_UPDATE, ("screen cleared"));
  1345. }
  1346.  
  1347. /*
  1348. **    InsStr(line, count)
  1349. **
  1350. **    Insert the count characters pointed to by line.
  1351. **
  1352. */
  1353.  
  1354. static void
  1355. InsStr(NCURSES_CH_T * line, int count)
  1356. {
  1357.     TR(TRACE_UPDATE, ("InsStr(%p,%d) called", line, count));
  1358.  
  1359.     /* Prefer parm_ich as it has the smallest cost - no need to shift
  1360.      * the whole line on each character. */
  1361.     /* The order must match that of InsCharCost. */
  1362.     if (parm_ich) {
  1363.     TPUTS_TRACE("parm_ich");
  1364.     tputs(tparm(parm_ich, count), count, _nc_outch);
  1365.     while (count) {
  1366.         PutAttrChar(CHREF(*line));
  1367.         line++;
  1368.         count--;
  1369.     }
  1370.     } else if (enter_insert_mode && exit_insert_mode) {
  1371.     TPUTS_TRACE("enter_insert_mode");
  1372.     putp(enter_insert_mode);
  1373.     while (count) {
  1374.         PutAttrChar(CHREF(*line));
  1375.         if (insert_padding) {
  1376.         TPUTS_TRACE("insert_padding");
  1377.         putp(insert_padding);
  1378.         }
  1379.         line++;
  1380.         count--;
  1381.     }
  1382.     TPUTS_TRACE("exit_insert_mode");
  1383.     putp(exit_insert_mode);
  1384.     } else {
  1385.     while (count) {
  1386.         TPUTS_TRACE("insert_character");
  1387.         putp(insert_character);
  1388.         PutAttrChar(CHREF(*line));
  1389.         if (insert_padding) {
  1390.         TPUTS_TRACE("insert_padding");
  1391.         putp(insert_padding);
  1392.         }
  1393.         line++;
  1394.         count--;
  1395.     }
  1396.     }
  1397.     position_check(SP->_cursrow, SP->_curscol, "InsStr");
  1398. }
  1399.  
  1400. /*
  1401. **    DelChar(count)
  1402. **
  1403. **    Delete count characters at current position
  1404. **
  1405. */
  1406.  
  1407. static void
  1408. DelChar(int count)
  1409. {
  1410.     int n;
  1411.  
  1412.     TR(TRACE_UPDATE, ("DelChar(%d) called, position = (%d,%d)", count,
  1413.               newscr->_cury, newscr->_curx));
  1414.  
  1415.     if (parm_dch) {
  1416.     TPUTS_TRACE("parm_dch");
  1417.     tputs(tparm(parm_dch, count), count, _nc_outch);
  1418.     } else {
  1419.     for (n = 0; n < count; n++) {
  1420.         TPUTS_TRACE("delete_character");
  1421.         putp(delete_character);
  1422.     }
  1423.     }
  1424. }
  1425.  
  1426. /*
  1427. **    _nc_outstr(char *str)
  1428. **
  1429. **    Emit a string without waiting for update.
  1430. */
  1431.  
  1432. NCURSES_EXPORT(void)
  1433. _nc_outstr(const char *str)
  1434. {
  1435.     (void) putp(str);
  1436.     _nc_flush();
  1437. }
  1438.  
  1439. /*
  1440.  * Physical-scrolling support
  1441.  *
  1442.  * This code was adapted from Keith Bostic's hardware scrolling
  1443.  * support for 4.4BSD curses.  I (esr) translated it to use terminfo
  1444.  * capabilities, narrowed the call interface slightly, and cleaned
  1445.  * up some convoluted tests.  I also added support for the memory_above
  1446.  * memory_below, and non_dest_scroll_region capabilities.
  1447.  *
  1448.  * For this code to work, we must have either
  1449.  * change_scroll_region and scroll forward/reverse commands, or
  1450.  * insert and delete line capabilities.
  1451.  * When the scrolling region has been set, the cursor has to
  1452.  * be at the last line of the region to make the scroll up
  1453.  * happen, or on the first line of region to scroll down.
  1454.  *
  1455.  * This code makes one aesthetic decision in the opposite way from
  1456.  * BSD curses.  BSD curses preferred pairs of il/dl operations
  1457.  * over scrolls, allegedly because il/dl looked faster.  We, on
  1458.  * the other hand, prefer scrolls because (a) they're just as fast
  1459.  * on many terminals and (b) using them avoids bouncing an
  1460.  * unchanged bottom section of the screen up and down, which is
  1461.  * visually nasty.
  1462.  *
  1463.  * (lav): added more cases, used dl/il when bot==maxy and in csr case.
  1464.  *
  1465.  * I used assumption that capabilities il/il1/dl/dl1 work inside
  1466.  * changed scroll region not shifting screen contents outside of it.
  1467.  * If there are any terminals behaving different way, it would be
  1468.  * necessary to add some conditions to scroll_csr_forward/backward.
  1469.  */
  1470.  
  1471. /* Try to scroll up assuming given csr (miny, maxy). Returns ERR on failure */
  1472. static int
  1473. scroll_csr_forward(int n, int top, int bot, int miny, int maxy, NCURSES_CH_T blank)
  1474. {
  1475.     int i;
  1476.  
  1477.     if (n == 1 && scroll_forward && top == miny && bot == maxy) {
  1478.     GoTo(bot, 0);
  1479.     UpdateAttrs(AttrOf(blank));
  1480.     TPUTS_TRACE("scroll_forward");
  1481.     tputs(scroll_forward, 0, _nc_outch);
  1482.     } else if (n == 1 && delete_line && bot == maxy) {
  1483.     GoTo(top, 0);
  1484.     UpdateAttrs(AttrOf(blank));
  1485.     TPUTS_TRACE("delete_line");
  1486.     tputs(delete_line, 0, _nc_outch);
  1487.     } else if (parm_index && top == miny && bot == maxy) {
  1488.     GoTo(bot, 0);
  1489.     UpdateAttrs(AttrOf(blank));
  1490.     TPUTS_TRACE("parm_index");
  1491.     tputs(tparm(parm_index, n, 0), n, _nc_outch);
  1492.     } else if (parm_delete_line && bot == maxy) {
  1493.     GoTo(top, 0);
  1494.     UpdateAttrs(AttrOf(blank));
  1495.     TPUTS_TRACE("parm_delete_line");
  1496.     tputs(tparm(parm_delete_line, n, 0), n, _nc_outch);
  1497.     } else if (scroll_forward && top == miny && bot == maxy) {
  1498.     GoTo(bot, 0);
  1499.     UpdateAttrs(AttrOf(blank));
  1500.     for (i = 0; i < n; i++) {
  1501.         TPUTS_TRACE("scroll_forward");
  1502.         tputs(scroll_forward, 0, _nc_outch);
  1503.     }
  1504.     } else if (delete_line && bot == maxy) {
  1505.     GoTo(top, 0);
  1506.     UpdateAttrs(AttrOf(blank));
  1507.     for (i = 0; i < n; i++) {
  1508.         TPUTS_TRACE("delete_line");
  1509.         tputs(delete_line, 0, _nc_outch);
  1510.     }
  1511.     } else
  1512.     return ERR;
  1513.  
  1514. #if NCURSES_EXT_FUNCS
  1515.     if (FILL_BCE()) {
  1516.     int j;
  1517.     for (i = 0; i < n; i++) {
  1518.         GoTo(bot - i, 0);
  1519.         for (j = 0; j < screen_columns; j++)
  1520.         PutChar(CHREF(blank));
  1521.     }
  1522.     }
  1523. #endif
  1524.     return OK;
  1525. }
  1526.  
  1527. /* Try to scroll down assuming given csr (miny, maxy). Returns ERR on failure */
  1528. /* n > 0 */
  1529. static int
  1530. scroll_csr_backward(int n, int top, int bot, int miny, int maxy,
  1531.             NCURSES_CH_T blank)
  1532. {
  1533.     int i;
  1534.  
  1535.     if (n == 1 && scroll_reverse && top == miny && bot == maxy) {
  1536.     GoTo(top, 0);
  1537.     UpdateAttrs(AttrOf(blank));
  1538.     TPUTS_TRACE("scroll_reverse");
  1539.     tputs(scroll_reverse, 0, _nc_outch);
  1540.     } else if (n == 1 && insert_line && bot == maxy) {
  1541.     GoTo(top, 0);
  1542.     UpdateAttrs(AttrOf(blank));
  1543.     TPUTS_TRACE("insert_line");
  1544.     tputs(insert_line, 0, _nc_outch);
  1545.     } else if (parm_rindex && top == miny && bot == maxy) {
  1546.     GoTo(top, 0);
  1547.     UpdateAttrs(AttrOf(blank));
  1548.     TPUTS_TRACE("parm_rindex");
  1549.     tputs(tparm(parm_rindex, n, 0), n, _nc_outch);
  1550.     } else if (parm_insert_line && bot == maxy) {
  1551.     GoTo(top, 0);
  1552.     UpdateAttrs(AttrOf(blank));
  1553.     TPUTS_TRACE("parm_insert_line");
  1554.     tputs(tparm(parm_insert_line, n, 0), n, _nc_outch);
  1555.     } else if (scroll_reverse && top == miny && bot == maxy) {
  1556.     GoTo(top, 0);
  1557.     UpdateAttrs(AttrOf(blank));
  1558.     for (i = 0; i < n; i++) {
  1559.         TPUTS_TRACE("scroll_reverse");
  1560.         tputs(scroll_reverse, 0, _nc_outch);
  1561.     }
  1562.     } else if (insert_line && bot == maxy) {
  1563.     GoTo(top, 0);
  1564.     UpdateAttrs(AttrOf(blank));
  1565.     for (i = 0; i < n; i++) {
  1566.         TPUTS_TRACE("insert_line");
  1567.         tputs(insert_line, 0, _nc_outch);
  1568.     }
  1569.     } else
  1570.     return ERR;
  1571.  
  1572. #if NCURSES_EXT_FUNCS
  1573.     if (FILL_BCE()) {
  1574.     int j;
  1575.     for (i = 0; i < n; i++) {
  1576.         GoTo(top + i, 0);
  1577.         for (j = 0; j < screen_columns; j++)
  1578.         PutChar(CHREF(blank));
  1579.     }
  1580.     }
  1581. #endif
  1582.     return OK;
  1583. }
  1584.  
  1585. /* scroll by using delete_line at del and insert_line at ins */
  1586. /* n > 0 */
  1587. static int
  1588. scroll_idl(int n, int del, int ins, NCURSES_CH_T blank)
  1589. {
  1590.     int i;
  1591.  
  1592.     if (!((parm_delete_line || delete_line) && (parm_insert_line || insert_line)))
  1593.     return ERR;
  1594.  
  1595.     GoTo(del, 0);
  1596.     UpdateAttrs(AttrOf(blank));
  1597.     if (n == 1 && delete_line) {
  1598.     TPUTS_TRACE("delete_line");
  1599.     tputs(delete_line, 0, _nc_outch);
  1600.     } else if (parm_delete_line) {
  1601.     TPUTS_TRACE("parm_delete_line");
  1602.     tputs(tparm(parm_delete_line, n, 0), n, _nc_outch);
  1603.     } else {            /* if (delete_line) */
  1604.     for (i = 0; i < n; i++) {
  1605.         TPUTS_TRACE("delete_line");
  1606.         tputs(delete_line, 0, _nc_outch);
  1607.     }
  1608.     }
  1609.  
  1610.     GoTo(ins, 0);
  1611.     UpdateAttrs(AttrOf(blank));
  1612.     if (n == 1 && insert_line) {
  1613.     TPUTS_TRACE("insert_line");
  1614.     tputs(insert_line, 0, _nc_outch);
  1615.     } else if (parm_insert_line) {
  1616.     TPUTS_TRACE("parm_insert_line");
  1617.     tputs(tparm(parm_insert_line, n, 0), n, _nc_outch);
  1618.     } else {            /* if (insert_line) */
  1619.     for (i = 0; i < n; i++) {
  1620.         TPUTS_TRACE("insert_line");
  1621.         tputs(insert_line, 0, _nc_outch);
  1622.     }
  1623.     }
  1624.  
  1625.     return OK;
  1626. }
  1627.  
  1628. NCURSES_EXPORT(int)
  1629. _nc_scrolln(int n, int top, int bot, int maxy)
  1630. /* scroll region from top to bot by n lines */
  1631. {
  1632.     NCURSES_CH_T blank = ClrBlank(stdscr);
  1633.     int i;
  1634.     bool cursor_saved = FALSE;
  1635.     int res;
  1636.  
  1637.     TR(TRACE_MOVE, ("mvcur_scrolln(%d, %d, %d, %d)", n, top, bot, maxy));
  1638.  
  1639. #if USE_XMC_SUPPORT
  1640.     /*
  1641.      * If we scroll, we might remove a cookie.
  1642.      */
  1643.     if (magic_cookie_glitch > 0) {
  1644.     return (ERR);
  1645.     }
  1646. #endif
  1647.  
  1648.     if (n > 0) {        /* scroll up (forward) */
  1649.     /*
  1650.      * Explicitly clear if stuff pushed off top of region might
  1651.      * be saved by the terminal.
  1652.      */
  1653.     res = scroll_csr_forward(n, top, bot, 0, maxy, blank);
  1654.  
  1655.     if (res == ERR && change_scroll_region) {
  1656.         if ((((n == 1 && scroll_forward) || parm_index)
  1657.          && (SP->_cursrow == bot || SP->_cursrow == bot - 1))
  1658.         && save_cursor && restore_cursor) {
  1659.         cursor_saved = TRUE;
  1660.         TPUTS_TRACE("save_cursor");
  1661.         tputs(save_cursor, 0, _nc_outch);
  1662.         }
  1663.         TPUTS_TRACE("change_scroll_region");
  1664.         tputs(tparm(change_scroll_region, top, bot), 0, _nc_outch);
  1665.         if (cursor_saved) {
  1666.         TPUTS_TRACE("restore_cursor");
  1667.         tputs(restore_cursor, 0, _nc_outch);
  1668.         } else {
  1669.         SP->_cursrow = SP->_curscol = -1;
  1670.         }
  1671.  
  1672.         res = scroll_csr_forward(n, top, bot, top, bot, blank);
  1673.  
  1674.         TPUTS_TRACE("change_scroll_region");
  1675.         tputs(tparm(change_scroll_region, 0, maxy), 0, _nc_outch);
  1676.         SP->_cursrow = SP->_curscol = -1;
  1677.     }
  1678.  
  1679.     if (res == ERR && _nc_idlok)
  1680.         res = scroll_idl(n, top, bot - n + 1, blank);
  1681.  
  1682.     /*
  1683.      * Clear the newly shifted-in text.
  1684.      */
  1685.     if (res != ERR
  1686.         && (non_dest_scroll_region || (memory_below && bot == maxy))) {
  1687.         NCURSES_CH_T blank2 = NewChar(BLANK_TEXT);
  1688.         if (bot == maxy && clr_eos) {
  1689.         GoTo(bot - n, 0);
  1690.         ClrToEOS(blank2);
  1691.         } else {
  1692.         for (i = 0; i < n; i++) {
  1693.             GoTo(bot - i, 0);
  1694.             ClrToEOL(blank2, FALSE);
  1695.         }
  1696.         }
  1697.     }
  1698.  
  1699.     } else {            /* (n < 0) - scroll down (backward) */
  1700.     res = scroll_csr_backward(-n, top, bot, 0, maxy, blank);
  1701.  
  1702.     if (res == ERR && change_scroll_region) {
  1703.         if (top != 0 && (SP->_cursrow == top || SP->_cursrow == top - 1)
  1704.         && save_cursor && restore_cursor) {
  1705.         cursor_saved = TRUE;
  1706.         TPUTS_TRACE("save_cursor");
  1707.         tputs(save_cursor, 0, _nc_outch);
  1708.         }
  1709.         TPUTS_TRACE("change_scroll_region");
  1710.         tputs(tparm(change_scroll_region, top, bot), 0, _nc_outch);
  1711.         if (cursor_saved) {
  1712.         TPUTS_TRACE("restore_cursor");
  1713.         tputs(restore_cursor, 0, _nc_outch);
  1714.         } else {
  1715.         SP->_cursrow = SP->_curscol = -1;
  1716.         }
  1717.  
  1718.         res = scroll_csr_backward(-n, top, bot, top, bot, blank);
  1719.  
  1720.         TPUTS_TRACE("change_scroll_region");
  1721.         tputs(tparm(change_scroll_region, 0, maxy), 0, _nc_outch);
  1722.         SP->_cursrow = SP->_curscol = -1;
  1723.     }
  1724.  
  1725.     if (res == ERR && _nc_idlok)
  1726.         res = scroll_idl(-n, bot + n + 1, top, blank);
  1727.  
  1728.     /*
  1729.      * Clear the newly shifted-in text.
  1730.      */
  1731.     if (res != ERR
  1732.         && (non_dest_scroll_region || (memory_above && top == 0))) {
  1733.         NCURSES_CH_T blank2 = NewChar(BLANK_TEXT);
  1734.         for (i = 0; i < -n; i++) {
  1735.         GoTo(i + top, 0);
  1736.         ClrToEOL(blank2, FALSE);
  1737.         }
  1738.     }
  1739.     }
  1740.  
  1741.     if (res == ERR)
  1742.     return (ERR);
  1743.  
  1744.     _nc_scroll_window(curscr, n, top, bot, blank);
  1745.  
  1746.     /* shift hash values too - they can be reused */
  1747.     _nc_scroll_oldhash(n, top, bot);
  1748.  
  1749.     return (OK);
  1750. }
  1751.  
  1752. NCURSES_EXPORT(void)
  1753. _nc_screen_resume(void)
  1754. {
  1755.     /* make sure terminal is in a sane known state */
  1756.     SP->_current_attr = A_NORMAL;
  1757.     newscr->_clear = TRUE;
  1758.  
  1759.     /* reset color pairs and definitions */
  1760.     if (SP->_coloron || SP->_color_defs)
  1761.     _nc_reset_colors();
  1762.  
  1763.     /* restore user-defined colors, if any */
  1764.     if (SP->_color_defs < 0) {
  1765.     int n;
  1766.     SP->_color_defs = -(SP->_color_defs);
  1767.     for (n = 0; n < SP->_color_defs; ++n) {
  1768.         if (SP->_color_table[n].init) {
  1769.         init_color(n,
  1770.                SP->_color_table[n].r,
  1771.                SP->_color_table[n].g,
  1772.                SP->_color_table[n].b);
  1773.         }
  1774.     }
  1775.     }
  1776.  
  1777.     if (exit_attribute_mode)
  1778.     putp(exit_attribute_mode);
  1779.     else {
  1780.     /* turn off attributes */
  1781.     if (exit_alt_charset_mode)
  1782.         putp(exit_alt_charset_mode);
  1783.     if (exit_standout_mode)
  1784.         putp(exit_standout_mode);
  1785.     if (exit_underline_mode)
  1786.         putp(exit_underline_mode);
  1787.     }
  1788.     if (exit_insert_mode)
  1789.     putp(exit_insert_mode);
  1790.     if (enter_am_mode && exit_am_mode)
  1791.     putp(auto_right_margin ? enter_am_mode : exit_am_mode);
  1792. }
  1793.  
  1794. NCURSES_EXPORT(void)
  1795. _nc_screen_init(void)
  1796. {
  1797.     _nc_screen_resume();
  1798. }
  1799.  
  1800. /* wrap up screen handling */
  1801. NCURSES_EXPORT(void)
  1802. _nc_screen_wrap(void)
  1803. {
  1804.     UpdateAttrs(A_NORMAL);
  1805. #if NCURSES_EXT_FUNCS
  1806.     if (SP->_coloron
  1807.     && !SP->_default_color) {
  1808.     NCURSES_CH_T blank = NewChar(BLANK_TEXT);
  1809.     SP->_default_color = TRUE;
  1810.     _nc_do_color(-1, 0, FALSE, _nc_outch);
  1811.     SP->_default_color = FALSE;
  1812.  
  1813.     mvcur(SP->_cursrow, SP->_curscol, screen_lines - 1, 0);
  1814.     SP->_cursrow = screen_lines - 1;
  1815.     SP->_curscol = 0;
  1816.  
  1817.     ClrToEOL(blank, TRUE);
  1818.     }
  1819. #endif
  1820.     if (SP->_color_defs) {
  1821.     _nc_reset_colors();
  1822.     }
  1823. }
  1824.  
  1825. #if USE_XMC_SUPPORT
  1826. NCURSES_EXPORT(void)
  1827. _nc_do_xmc_glitch(attr_t previous)
  1828. {
  1829.     attr_t chg = XMC_CHANGES(previous ^ SP->_current_attr);
  1830.  
  1831.     while (chg != 0) {
  1832.     if (chg & 1) {
  1833.         SP->_curscol += magic_cookie_glitch;
  1834.         if (SP->_curscol >= SP->_columns)
  1835.         wrap_cursor();
  1836.         TR(TRACE_UPDATE, ("bumped to %d,%d after cookie", SP->_cursrow, SP->_curscol));
  1837.     }
  1838.     chg >>= 1;
  1839.     }
  1840. }
  1841. #endif /* USE_XMC_SUPPORT */
  1842.